﻿// <copyright file="Program.cs" company="Hallvard Korsgaard">
//  Microsoft Public License (Ms-PL). See license details here: http://enoceannet.codeplex.com/license
// </copyright>
// <author>Hallvard Korsgaard</author>

namespace ReceiveVerbose
{
  using System;
  using System.Collections.Generic;
  using System.Net;
  using System.Text;
  using EnOcean;
  using EnOcean.Message;
  using EnOcean.Radio.Sensor;
  using StcEthernet;
  using StcEthernet.Management;

  /// <summary>
  /// Shows how to receive telegrams from STC-Ethernet transievers. Displaying more details.
  /// </summary>
  public class Program
  {
    /// <summary>
    /// This variable is used for synchronizing write access to the console.
    /// </summary>
    private static readonly object lck = new object();

    /// <summary>
    /// Used for keeping track of the transievers so we can clean up before exiting.
    /// </summary>
    private static readonly List<Transiever> transievers = new List<Transiever>();

    /// <summary>
    /// Application starts here.
    /// </summary>
    public static void Main()
    {
      IPAddress serverAddress;
      IPEndPoint serverEndpoint;
      Server server;

      // Create a new server listening on TCP port 4000.
      serverAddress = IPAddress.Parse("192.168.188.102");
      serverEndpoint = new IPEndPoint(serverAddress, 4000);
      server = new Server(serverEndpoint);

      // Hook up the event handlers.
      server.TransieverConnected += new EventHandler<TransieverConnectedEventArgs>(TransieverConnected);
      server.TransieverInitialized += new EventHandler<TransieverInitializedEventArgs>(TransieverInitialized);

      // Start to accept connections.
      server.Accept();

      lock (lck)
      {
        Console.WriteLine();
        Console.WriteLine("Press any key to exit.");
        Console.WriteLine();
      }

      Console.ReadKey();

      // Stop the server
      server.Close();

      // Close Transiever objects
      lock (transievers)
      {
        foreach (Transiever t in transievers)
        {
          t.Close();
        }
      }
    }

    /// <summary>
    /// This event handler will be called when a transiever connects.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void TransieverConnected(object sender, TransieverConnectedEventArgs e)
    {
      lock (lck)
      {
        Console.WriteLine("Transiever connected.");
      }

      lock (transievers)
      {
        transievers.Add(e.Transiever);
      }
    }

    /// <summary>
    /// This event handler will be called when the Transiever object is ready for use.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void TransieverInitialized(object sender, TransieverInitializedEventArgs e)
    {
      lock (lck)
      {
        Console.WriteLine("Transiever initialized.");
        Console.WriteLine("Base ID          : " + e.Transiever.BaseId.ToString("X8"));
        Console.WriteLine("Modem on         : " + e.Transiever.IsModemOn);
        Console.WriteLine("Modem ID         : " + e.Transiever.ModemId.ToString("X4"));
        Console.WriteLine("Operating mode   : " + e.Transiever.OperatingMode);
        Console.WriteLine("Radio sensitivity: " + e.Transiever.RadioSensitivity);
        Console.WriteLine("Software version : " + e.Transiever.SoftwareVersion);
      }

      // Hooking up event handlers.
      e.Transiever.TelegramReceived += new EventHandler<TelegramReceivedEventArgs>(Transiever_TelegramReceived);
      e.Transiever.ManagementTelegramReceived += new EventHandler<ManagementTelegramReceivedEventArgs>(Transiever_ManagementTelegramReceived);
      e.Transiever.UnknownTelegramReceived += new EventHandler<UnknownDataReceivedEventArgs>(Transiever_UnknownTelegramReceived);
      e.Transiever.ResponseTimeout += new EventHandler<ResponseTimeoutEventArgs>(Transiever_ResponseTimeout);
      e.Transiever.TransieverDisconnected += new EventHandler<TransieverDisconnectedEventArgs>(Transiever_TransieverDisconnected);
    }

    /// <summary>
    /// The event fires when a telegram is received from the transiever.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void Transiever_TelegramReceived(object sender, TelegramReceivedEventArgs e)
    {
      lock (lck)
      {
        Console.WriteLine();
        Console.WriteLine(DateTime.Now);
        Console.WriteLine("Telegram: " + e.Telegram.GetType().ToString() + " " + e.Telegram);

        if (e.Telegram is InformIdBase)
        {
          DisplayInformIdBase((InformIdBase)e.Telegram);
        }
        else if (e.Telegram is InformInit)
        {
          DisplayInformInit((InformInit)e.Telegram);
        }
        else if (e.Telegram is InformModemStatus)
        {
          DisplayInformModemStatus((InformModemStatus)e.Telegram);
        }
        else if (e.Telegram is InformRadioSensitivity)
        {
          DisplayInformRadioSensitivity((InformRadioSensitivity)e.Telegram);
        }
        else if (e.Telegram is InformSoftwareVersion)
        {
          DisplayInformSoftwareVersion((InformSoftwareVersion)e.Telegram);
        }
        else if (e.Telegram is RepeatedSwitchNTelegram)
        {
          DisplayRepeatedSwitchNTelegram((RepeatedSwitchNTelegram)e.Telegram, e.Channel);
        }
        else if (e.Telegram is RepeatedSwitchUTelegram)
        {
          DisplayRepeatedSwitchUTelegram((RepeatedSwitchUTelegram)e.Telegram, e.Channel);
        }
        else if (e.Telegram is OneByteSensorTelegram)
        {
          DisplayOneByteSensorTelegram((OneByteSensorTelegram)e.Telegram, e.Channel);
        }
        else if (e.Telegram is FourByteSensorDataTelegram)
        {
          DisplayFourByteSensorDataTelegram((FourByteSensorDataTelegram)e.Telegram, e.Channel);
        }
        else if (e.Telegram is FourByteSensorTeachTelegram)
        {
          DisplayFourByteSensorTeachTelegram((FourByteSensorTeachTelegram)e.Telegram, e.Channel);
        }
      }
    }

    /// <summary>
    /// The event fires when a management telegram is received from the transiever.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void Transiever_ManagementTelegramReceived(object sender, ManagementTelegramReceivedEventArgs e)
    {
      lock (lck)
      {
        Console.WriteLine();
        Console.WriteLine(DateTime.Now);
        Console.WriteLine("Management telegram: " + e.Telegram.GetType().ToString() + " " + e.Telegram);

        if (e.Telegram is InformMode)
        {
          DisplayInformMode((InformMode)e.Telegram);
        }
        else if (e.Telegram is InformTimeError)
        {
          DisplayInformTimeError((InformTimeError)e.Telegram);
        }
      }
    }

    /// <summary>
    /// This event handler will be called when if unrecognizable information is received.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void Transiever_UnknownTelegramReceived(object sender, UnknownDataReceivedEventArgs e)
    {
      lock (lck)
      {
        Console.WriteLine();
        Console.WriteLine(DateTime.Now);
        Console.WriteLine("Unknown telegram: " + Encoding.ASCII.GetString(e.Data));
        Console.WriteLine(e.Exception);
      }
    }

    /// <summary>
    /// This event handler will be called if an expected response message is not received when sending a message.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void Transiever_ResponseTimeout(object sender, ResponseTimeoutEventArgs e)
    {
      lock (lck)
      {
        Console.WriteLine();
        Console.WriteLine(DateTime.Now);
        Console.WriteLine("Response timeout.");
      }
    }

    /// <summary>
    /// This event handler will be called when a transiever disconnects.
    /// The event handler code must be thread safe because several events can fire at the same time.
    /// </summary>
    /// <param name="sender">The object that sent the event.</param>
    /// <param name="e">The event data.</param>
    private static void Transiever_TransieverDisconnected(object sender, TransieverDisconnectedEventArgs e)
    {
      lock (lck)
      {
        Console.WriteLine();
        Console.WriteLine(DateTime.Now);
        Console.WriteLine("Transiever disconnected.");
      }

      lock (transievers)
      {
        transievers.Remove(e.Transiever);
      }

      // The Transiever object will call the Close method on itself after this event handler returns.
    }

    /// <summary>
    /// Prints a InformIdBase telegram to the console.
    /// </summary>
    /// <param name="t">A InformIdBase telegram.</param>
    private static void DisplayInformIdBase(InformIdBase t)
    {
      lock (lck)
      {
        Console.WriteLine("Transiever ID base: " + t.IdBase.ToString("X8"));
      }
    }

    /// <summary>
    /// Prints a InformInit telegram to the console.
    /// </summary>
    /// <param name="t">A InformInit telegram.</param>
    private static void DisplayInformInit(InformInit t)
    {
      lock (lck)
      {
        Console.WriteLine("Text: " + t.Text);
      }
    }

    /// <summary>
    /// Prints a InformModemStatus telegram to the console.
    /// </summary>
    /// <param name="t">A InformModemStatus telegram.</param>
    private static void DisplayInformModemStatus(InformModemStatus t)
    {
      lock (lck)
      {
        Console.WriteLine("Modem ID: " + t.ModemId.ToString("X4"));
        Console.WriteLine("Modem on: " + t.IsModemOn);
      }
    }

    /// <summary>
    /// Prints a InformRadioSensitivity telegram to the console.
    /// </summary>
    /// <param name="t">A InformRadioSensitivity telegram.</param>
    private static void DisplayInformRadioSensitivity(InformRadioSensitivity t)
    {
      lock (lck)
      {
        Console.WriteLine("Radio sensitivity: " + t.Sensitivity);
      }
    }

    /// <summary>
    /// Prints a InformSoftwareVersion telegram to the console.
    /// </summary>
    /// <param name="t">A InformSoftwareVersion telegram.</param>
    private static void DisplayInformSoftwareVersion(InformSoftwareVersion t)
    {
      lock (lck)
      {
        Console.WriteLine("Software version: {0}.{1}.{2}.{3}", t.VersionPosition1, t.VersionPosition2, t.VersionPosition3, t.VersionPosition4);
      }
    }

    /// <summary>
    /// Prints a RepeatedSwitchNTelegram telegram to the console.
    /// </summary>
    /// <param name="t">A RepeatedSwitchNTelegram telegram.</param>
    /// <param name="channel">The channel that received the telegram or 0xFF if in gateway mode.</param>
    private static void DisplayRepeatedSwitchNTelegram(RepeatedSwitchNTelegram t, byte channel)
    {
      lock (lck)
      {
        Console.WriteLine("Id               : " + t.Id.ToString("X8"));
        Console.WriteLine("SwitchType       : " + t.SwitchType);
        Console.WriteLine("RepeatCounter    : " + t.RepeatCounter);
        Console.WriteLine("EnergyBowPressed : " + t.IsEnergyBowPressed);
        Console.WriteLine("FirstButtonId    : " + t.FirstButtonId);
        Console.WriteLine("FirstButtonState : " + t.FirstButtonState);

        if (t.HasSecondAction)
        {
          Console.WriteLine("SecondAction     : " + t.HasSecondAction);
          Console.WriteLine("SecondButtonId   : " + t.SecondButtonId);
          Console.WriteLine("SecondButtonState: " + t.SecondButtonState);
        }

        if (channel != 0xFF)
        {
          Console.WriteLine("Channel          : " + channel);
        }
      }
    }

    /// <summary>
    /// Prints a RepeatedSwitchUTelegram telegram to the console.
    /// </summary>
    /// <param name="t">A RepeatedSwitchUTelegram telegram.</param>
    /// <param name="channel">The channel that received the telegram or 0xFF if in gateway mode.</param>
    private static void DisplayRepeatedSwitchUTelegram(RepeatedSwitchUTelegram t, byte channel)
    {
      lock (lck)
      {
        Console.WriteLine("Id              : " + t.Id.ToString("X8"));
        Console.WriteLine("SwitchType      : " + t.SwitchType);
        Console.WriteLine("RepeatCounter   : " + t.RepeatCounter);
        Console.WriteLine("EnergyBowPressed: " + t.IsEnergyBowPressed);
        Console.WriteLine("Buttons pressed : " + t.Buttons);

        if (channel != 0xFF)
        {
          Console.WriteLine("Channel         : " + channel);
        }
      }
    }

    /// <summary>
    /// Prints a OneByteSensorTelegram telegram to the console.
    /// </summary>
    /// <param name="t">A OneByteSensorTelegram telegram.</param>
    /// <param name="channel">The channel that received the telegram or 0xFF if in gateway mode.</param>
    private static void DisplayOneByteSensorTelegram(OneByteSensorTelegram t, byte channel)
    {
      lock (lck)
      {
        Console.WriteLine("Id           : " + t.Id.ToString("X8"));
        Console.WriteLine("RepeatCounter: " + t.RepeatCounter);
        Console.WriteLine("Data         : " + t.Data.ToString("X2"));

        if (channel != 0xFF)
        {
          Console.WriteLine("Channel          : " + channel);
        }
      }
    }

    /// <summary>
    /// Prints a FourByteSensorDataTelegram telegram to the console.
    /// </summary>
    /// <param name="t">A FourByteSensorDataTelegram telegram.</param>
    /// <param name="channel">The channel that received the telegram or 0xFF if in gateway mode.</param>
    private static void DisplayFourByteSensorDataTelegram(FourByteSensorDataTelegram t, byte channel)
    {
      lock (lck)
      {
        Console.WriteLine("Id           : " + t.Id.ToString("X8"));
        Console.WriteLine("RepeatCounter: " + t.RepeatCounter);
        Console.WriteLine("LRN pressed  : " + t.IsLearnButtonPressed);

        // TODO: Update the IDs below with you own sensor IDs. The ConvertRange method is used for converting raw sensor data to real world values.
        if (t.Id == 0x00020011)
        {
          // Temp & Humidity sensor.
          Console.WriteLine("Temp         : " + Math.Round(Telegram.ConvertRange(t.Data1, 0, 250, 0, 40), 2));
          Console.WriteLine("Humidity     : " + Math.Round(Telegram.ConvertRange(t.Data2, 0, 250, 0, 100), 2));
        }
        else if (t.Id == 0x00020022)
        {
          // Outdoor temp sensor.
          Console.WriteLine("Outdoor temp : " + Math.Round(Telegram.ConvertRange(t.Data1, 255, 0, -20, 60), 2));
        }
        else if (t.Id == 0x0001021D)
        {
          // Motion & Light sensor
          Console.WriteLine("Motion       : " + ((t.Data0 & 0x2) == 0x0));
          Console.WriteLine("Brightness   : " + Math.Round(Telegram.ConvertRange(t.Data2, 0, 255, 0, 512), 2));
          Console.WriteLine("Voltage      : " + Math.Round(Telegram.ConvertRange(t.Data3, 0, 255, 0, 5.12m), 2));
        }
        else if (t.Id == 0x000142F6)
        {
          // Outdoor light sensor.
          Console.WriteLine("Brightness   : " + Math.Round(Telegram.ConvertRange(t.Data2, 0, 255, 300, 30000), 2));
        }
        else
        {
          Console.WriteLine("Data 0       : " + t.Data0.ToString("X2"));
          Console.WriteLine("Data 1       : " + t.Data1.ToString("X2"));
          Console.WriteLine("Data 2       : " + t.Data2.ToString("X2"));
          Console.WriteLine("Data 3       : " + t.Data3.ToString("X2"));
        }

        if (channel != 0xFF)
        {
          Console.WriteLine("Channel          : " + channel);
        }
      }
    }

    /// <summary>
    /// Prints a FourByteSensorTeachTelegram telegram to the console.
    /// </summary>
    /// <param name="t">A FourByteSensorTeachTelegram telegram.</param>
    /// <param name="channel">The channel that received the telegram or 0xFF if in gateway mode.</param>
    private static void DisplayFourByteSensorTeachTelegram(FourByteSensorTeachTelegram t, byte channel)
    {
      lock (lck)
      {
        Console.WriteLine("Id           : " + t.Id.ToString("X8"));
        Console.WriteLine("RepeatCounter: " + t.RepeatCounter);
        Console.WriteLine("Function     : " + t.Function);
        Console.WriteLine("Device type  : " + t.DeviceType);
        Console.WriteLine("Manufacturer : " + t.Manufacturer);

        if (channel != 0xFF)
        {
          Console.WriteLine("Channel          : " + channel);
        }
      }
    }

    /// <summary>
    /// Prints a InformMode telegram to the console.
    /// </summary>
    /// <param name="t">A InformMode telegram.</param>
    private static void DisplayInformMode(InformMode t)
    {
      lock (lck)
      {
        Console.WriteLine("Operating mode: " + t.Mode);
      }
    }

    /// <summary>
    /// Prints a InformTimeError telegram to the console.
    /// </summary>
    /// <param name="t">A InformTimeError telegram.</param>
    private static void DisplayInformTimeError(InformTimeError t)
    {
      lock (lck)
      {
        Console.WriteLine("Channel                  : " + t.Channel);
        Console.WriteLine("Has failed               : " + t.HasFailed);
        Console.WriteLine("Sensor ID                : " + t.Id.ToString("X8"));
        Console.WriteLine("Is monitor timer on      : " + t.IsMonitorTimerOn);
        Console.WriteLine("Monitor interval         : " + t.MonitorInterval);
        Console.WriteLine("Org                      : " + t.Org);
        Console.WriteLine("Failure from time to time: " + t.SensorFailureFromTimeToTime);
      }
    }
  }
}
